<template>
  <view class="box">
    <view class="main-header">
      <!-- 游戏时间 -->
      <view class="game-time">
        <view class="game-time-col">
          <text class="game-time-name">倒计时：</text>
          <text class="game-time-value">00:{{ gameTime < 10 ? `0${gameTime}` : gameTime }}</text>
        </view>
        <view class="game-time-col">
          <text class="game-time-name">分数：</text>
          <text class="game-time-value">{{ score }}</text>
        </view>
      </view>
    </view>
    <view class="main">
      <!-- 匹配区域 -->
      <view class="match-box">
        <view class="match-border">
          <view
            v-for="(target, index) in targetData"
            :key="index"
            :class="`target ${target.type}`"
            :style="{
              left: `${target.left}rpx`,
              top: `${target.top}rpx`,
              backgroundColor: `${target.color}`,
              opacity: `${target.opacity}`
            }"
          />
        </view>
      </view>
      <!-- 图形选择 -->
      <view class="shape-box">
        <view class="shape-content-box">
          <view
            v-for="(item, index) in shapeData"
            :key="index"
            @touchstart="touchStartEvent($event, item)"
            @touchmove="touchMoveEvent($event, item)"
            @touchend="touchEndEvent($event, item, index)"
            :class="item.type"
            :style="{
              backgroundColor: item.color,
              transform: `translate3d(${item.translateX}rpx, ${item.translateY}rpx, 0)`,
              transition: item.transition,
              position: 'absolute',
              zIndex: 2
            }"
          />
        </view>
      </view>
    </view>
    <!-- 难度选择 -->
    <view class="mode" v-if="!isTypeSelect">
      <view class="mode-content">
        <text class="mode-name" style="margin-bottom: 128rpx">选择难度</text>
        <view @tap="changeMode('easy')" class="mode-easy">
          <image
            class="back-image"
            src="@/static/image/game8/btn-background.png"
            mode="scaleToFill"
          />
          <text class="mode-name">简 单</text>
        </view>
        <view @tap="changeMode('primary')" class="mode-primary">
          <image
            class="back-image"
            src="@/static/image/game8/btn-background.png"
            mode="scaleToFill"
          />
          <text class="mode-name">普 通</text>
        </view>
        <view @tap="changeMode('difficult')" class="mode-difficult">
          <image
            class="back-image"
            src="@/static/image/game8/btn-background.png"
            mode="scaleToFill"
          />
          <text class="mode-name">困 难</text>
        </view>
      </view>
    </view>
    <!-- 开始界面 -->
    <view
      class="start-box"
      style="align-items: flex-end; padding-bottom: 328rpx"
      v-if="isStart && isTypeSelect"
    >
      <view @tap="startGame" class="start-btn">
        <image
          class="back-image"
          src="@/static/image/game8/btn-background.png"
          mode="scaleToFill"
        />
        <text class="mode-name">开始游戏</text>
      </view>
    </view>
    <!-- 开始倒计时 -->
    <view class="start-down" v-if="startDown">
      <text class="start-down-name">{{ startDownCount }}</text>
    </view>
    <!-- 游戏结束 -->
    <view class="game-over" v-if="isGameOver">
      <view class="over-box">
        <view class="over-main">
          <text class="over-name">完成训练!</text>
          <view class="game-over-time-box">
            <view style="position: relative; display: flex; margin: 0 8rpx 0 0">
              <text class="game-over-time">{{ score + gameTimeToScore }}</text>
            </view>
            <view style="position: relative; display: flex">
              <text class="time-name">分</text>
            </view>
          </view>
        </view>
        <view class="over-btn">
          <view @tap="resetGame" class="reset-btn" style="margin-bottom: 48rpx">
            <image
              class="back-image"
              src="@/static/image/game8/btn-background.png"
              mode="scaleToFill"
            />
            <text class="mode-name">再次训练</text>
          </view>
          <view @tap="quitGame" class="quit-btn">
            <image
              class="back-image"
              src="@/static/image/game8/btn-background.png"
              mode="scaleToFill"
            />
            <text class="mode-name">退出训练</text>
          </view>
        </view>
      </view>
    </view>
  </view>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'

type startMoveObj = {
  x: number
  y: number
}

type shapeObj = {
  width: number
  height: number
  translateX: number
  translateY: number
  color: string
  startMove: startMoveObj
  startPos: startMoveObj
  type: string
  transition: string
}

let touchAudio: any = null
const isTypeSelect = ref<boolean>(false) // 是否选择
const isStart = ref<boolean>(false) // 是否开始
const isGameOver = ref<boolean>(false) // 是否结束
const startDown = ref<boolean>(false)
const startDownCount = ref<number>(3)

const gameTime = ref<number>(60)
const gameTimeToScore = ref<number>(0)
let timer: any = null
const score = ref<number>(0)
const modeType = ref<string>('easy')

const previousDraggedShape = ref<shapeObj>() // 上一次拖动的图形
const underfindShape = ref<shapeObj>()

// 图形对应的颜色数据
const shapeColorData = ref<any>({
  square: [],
  circle: [],
  triangle: [],
  hexagon: []
})

const shapeData = ref<shapeObj[]>([])

type targetObj = {
  left: number
  top: number
  color: string
  type: string
  value: number
  opacity: number
  isSelect: boolean
}

const shapeFormTargetData = ref<targetObj[]>([]) // 剩余图形数据  来源是 targetData打乱的数据
const targetData = ref<targetObj[]>([
  {
    left: 30,
    top: 80,
    color: 'yellow',
    type: 'square',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 240,
    top: 80,
    color: 'blue',
    type: 'hexagon',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 462,
    top: 80,
    color: 'pink',
    type: 'circle',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 30,
    top: 316,
    color: 'yellow',
    type: 'circle',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 240,
    top: 316,
    color: 'blue',
    type: 'triangle',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 462,
    top: 316,
    color: 'pink',
    type: 'square',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 30,
    top: 552,
    color: 'yellow',
    type: 'hexagon',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 240,
    top: 552,
    color: 'blue',
    type: 'circle',
    value: 0,
    opacity: 1,
    isSelect: false
  },
  {
    left: 462,
    top: 552,
    color: 'pink',
    type: 'triangle',
    value: 0,
    opacity: 1,
    isSelect: false
  }
])

type ThrottleFn = (e: any, domData: any) => void
// 节流函数
const throttle = (fn: ThrottleFn, limit: number): ThrottleFn => {
  let inThrottle: boolean = false

  return function (this: any, ...args: [any, any]): void {
    if (!inThrottle) {
      fn.apply(this, args)
      inThrottle = true

      setTimeout(() => {
        inThrottle = false
      }, limit)
    }
  }
}

// 随机生成9个图形
const generateMatchShape = () => {
  const shapeAttr: any = [
    {
      type: 'square',
      value: 1
    },
    {
      type: 'square',
      value: 2
    },
    {
      type: 'circle',
      value: 1
    },
    {
      type: 'circle',
      value: 2
    },
    {
      type: 'triangle',
      value: 1
    },
    {
      type: 'triangle',
      value: 2
    },
    {
      type: 'hexagon',
      value: 1
    },
    {
      type: 'hexagon',
      value: 2
    }
  ]
  const randIndex = Math.floor(Math.random() * shapeAttr.length)
  shapeAttr.push({
    type: shapeAttr[randIndex].type,
    value: 1
  })

  const typeData = shuffleArray(shapeAttr)
  targetData.value.forEach((item, index) => {
    item.type = typeData[index].type
    item.value = typeData[index].value
    item.opacity = 0.2
    item.isSelect = false
  })

  changeColor()
  shapeFormTargetData.value = targetData.value.slice()
}

const shuffleArray = (data: any) => {
  const arr = data.slice()
  for (let i = arr.length - 1; i > 0; i--) {
    const randomIndex = Math.floor(Math.random() * (i + 1))
    ;[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]
  }

  for (let i = 1; i < arr.length; i++) {
    if (arr[i].type === arr[i - 1].type) {
      for (let j = i + 1; j < arr.length; j++) {
        if (arr[j].type !== arr[i - 1].type) {
          // 交换位置
          ;[arr[i], arr[j]] = [arr[j], arr[i]]
          break
        }
      }
    }
  }

  return arr
}

onLoad(() => {
  score.value = 0
  if (timer) {
    clearInterval(timer)
  }

  isGameOver.value = false
  isStart.value = false
  isTypeSelect.value = false
  startDown.value = false
  startDownCount.value = 3
  shapeColorData.value = {
    square: [],
    circle: [],
    triangle: [],
    hexagon: []
  }

  if (touchAudio) {
    try {
      touchAudio.stop()
      //   touchAudio.destroy()
      touchAudio = null
    } catch (e) {
      // handle the exception
    }
  }

  generateMatchShape() // 生成随机图形
})

const colors = ['#F13434', '#FFD600', '#3C73FF', '#44D84A'] // 颜色

const changeMode = (e: string) => {
  isGameOver.value = false
  if (timer) {
    clearInterval(timer)
  }

  modeType.value = e

  isStart.value = true
  isGameOver.value = false
  isTypeSelect.value = true
}

// 颜色重新设定
const changeColor = () => {
  targetData.value.map((item) => {
    item.color = colors[Math.floor(Math.random() * colors.length)]
  })
}

let startDownTimer: any = null
// 开始游戏
const startGame = () => {
  isStart.value = false
  isGameOver.value = false
  score.value = 0
  startDown.value = true
  startDownCount.value = 3

  startDownTimer = setInterval(() => {
    startDownCount.value--
    if (startDownCount.value < 1) {
      clearInterval(startDownTimer)
      startDown.value = false
      shapeData.value = []
      generateShape()

      timer = setInterval(() => {
        gameTime.value--
        if (gameTime.value <= 0) {
          gameTimeToScore.value = 0
          console.log('游戏结束')
          clearInterval(timer)
          isGameOver.value = true
        }
      }, 1000)
    }
  }, 1000)
}

const resetGame = () => {
  if (touchAudio) {
    try {
      touchAudio.stop()
    } catch (e) {
      // handle the exception
    }
  }
  isGameOver.value = false
  isStart.value = true
  gameTime.value = 60
  gameTimeToScore.value = 0

  shapeData.value = []
  generateMatchShape()
}
const quitGame = () => {
  if (touchAudio) {
    try {
      touchAudio.stop()
      //   touchAudio.destroy()
      touchAudio = null
    } catch (e) {
      // handle the exception
    }
  }
  uni.navigateTo({
    url: `/pages/index/index`
  })
}

const touchStartEvent = (e: any, domData: shapeObj) => {
  if (previousDraggedShape.value) {
    previousDraggedShape.value.translateX = previousDraggedShape.value.startPos.x
    previousDraggedShape.value.translateY = previousDraggedShape.value.startPos.y
    previousDraggedShape.value.transition = ''
  }
  previousDraggedShape.value = domData

  domData.transition = 'none'
  const { clientX, clientY } = e.touches[0]
  const clientX_rpx = px2upx(clientX)
  const clientY_rpx = px2upx(clientY)
  domData.startMove = {
    x: clientX_rpx - domData.translateX,
    y: clientY_rpx - domData.translateY
  }
}

const touchMoveEvent = throttle((e, domData) => {
  const touches = e.touches
  const { clientX: oneClientX, clientY: oneClientY } = touches[0]
  const x = px2upx(oneClientX) // 转换为 rpx
  const y = px2upx(oneClientY) // 转换为 rpx
  domData.translateX = x - domData.startMove.x
  domData.translateY = y - domData.startMove.y
}, 10)

const touchEndEvent = (e: any, shape: any, index: any) => {
  shape.transition = 'transform .3s ease'
  const { clientX, clientY } = e.changedTouches[0]
  const x = px2upx(clientX) // 转换为 rpx
  const y = px2upx(clientY) // 转换为 rpx

  const marginOfError = 5 // 误差范围
  const originalColor = shape.color // 初始颜色

  if (gameTime.value === 0) {
    gameTimeToScore.value = 0
    console.log('游戏结束')
    shape.color = 'red' // 设置为红色
    setTimeout(() => {
      shape.color = originalColor
    }, 100)
    // 没有匹配成功，可以恢复初始位置
    shape.translateX = shape.startPos.x
    shape.translateY = shape.startPos.y
  } else {
    // 检查是否进入目标区域
    const matchedTarget = targetData.value.find((target) => {
      const targetLeft = target.left + 50 - marginOfError
      const targetRight = target.left + 50 + shape.width + marginOfError
      const targetTop = target.top - marginOfError + 176
      const targetBottom = target.top + shape.height + marginOfError + 176

      const shapeLeft = x
      const shapeRight = x + shape.width

      const shapeTop = y
      const shapeBottom = y + shape.height

      // 检查图形的上边界和左边界是否在目标区域内
      const isWithinTopLeftBounds =
        shapeLeft >= targetLeft &&
        shapeLeft < targetRight &&
        shapeTop >= targetTop &&
        shapeTop < targetBottom

      // 检查图形的下边界和右边界是否在目标区域内
      const isWithinBottomRightBounds =
        shapeRight <= targetRight &&
        shapeRight > targetLeft &&
        shapeBottom <= targetBottom &&
        shapeBottom > targetTop

      return (
        shape.type === target.type && // 匹配形状类型
        (isWithinTopLeftBounds || isWithinBottomRightBounds) &&
        // isWithinXRange &&
        // isWithinYRange &&
        !target.isSelect
      )
    })

    if (matchedTarget) {
      const matchedIndex = targetData.value.indexOf(matchedTarget)
      targetData.value[matchedIndex].isSelect = true
      targetData.value[matchedIndex].color = originalColor
      targetData.value[matchedIndex].opacity = 1
      previousDraggedShape.value = underfindShape.value
      playAudio('success')
      // 匹配成功，图形放置在目标区域
      score.value++

      shape.color = 'green' // 设置为绿色
      setTimeout(() => {
        if (!isGameOver.value) {
          // 仅当游戏没有结束时继续改变颜色
          shape.color = originalColor
          shapeData.value.splice(index, 1)
          if (shapeFormTargetData.value.length > 0) {
            const data: any = shapeFormTargetData.value[0]
            shapeFormTargetData.value = shapeFormTargetData.value.slice(1)
            addShape(matchColor(data))
          } else {
            if (shapeData.value.length === 0) {
              gameTimeToScore.value = Math.round(gameTime.value / 10)
              console.log('游戏结束')
              clearInterval(timer)
              isGameOver.value = true
            }
          }
        }
      }, 100)
    } else {
      playAudio('error')
      shape.color = 'red' // 设置为红色
      setTimeout(() => {
        shape.color = originalColor
      }, 100)
      // 没有匹配成功，可以恢复初始位置
      shape.translateX = shape.startPos.x
      shape.translateY = shape.startPos.y
    }
  }
}

// 随机三种生成图形
const generateShape = () => {
  const length: any = shapeFormTargetData.value.length
  if (length >= 3) {
    shapeFormTargetData.value = shuffleArray(shapeFormTargetData.value)
    // 取前三个数据
    const sliceData: any = shapeFormTargetData.value.slice(0, 3)
    for (let i = 0; i < sliceData.length; i++) {
      if (modeType.value === 'difficult') {
        addShape(matchColor(sliceData[i]))
      } else {
        addShape(sliceData[i])
      }
    }
    // 去除前三个数据
    shapeFormTargetData.value = shapeFormTargetData.value.slice(3)
  }
}

// 匹配不同的颜色
const matchColor = (data: targetObj) => {
  const newdata = JSON.parse(JSON.stringify(data))
  switch (modeType.value) {
    case 'easy':
      // easy
      break
    case 'primary':
      newdata.color = getRandomExcluding([newdata.color])
      break
    case 'difficult':
      newdata.color = getRandomExcluding(getSomeColor(newdata.type))
      break
    default:
      // default
      break
  }

  return newdata
}

const getSomeColor = (type: string) => {
  const someTypeData = targetData.value.filter((item: targetObj) => item.type === type)
  let someTypeColor = someTypeData.map((item: targetObj) => item.color)
  someTypeColor = [...new Set(someTypeColor)] // 已存在的颜色
  return someTypeColor
}

// 随机生成一个值 不与现在的值相同
const getRandomExcluding = (color: string[]) => {
  let random
  do {
    random = Math.floor(Math.random() * 4)
  } while (color.includes(colors[random]))
  return colors[random]
}

// 增加图形
const addShape = (data: targetObj) => {
  shapeData.value.push({
    width: 164,
    height: 164,
    translateX: 0,
    translateY: 44,
    color: data.color,
    startMove: {
      x: 0,
      y: 0
    },
    startPos: {
      x: 0,
      y: 44
    },
    type: data.type,
    transition: ''
  })

  shapeData.value.map((item: shapeObj, index) => {
    item.translateX = 204 * index
    item.startPos.x = 204 * index
  })
}

const playAudio = (type: any) => {
  const playList: any = {
    success: '/static/video/game2/success.mp3',
    error: '/static/video/game2/error.mp3'
  }

  if (touchAudio) {
    try {
      touchAudio.stop()
      //   touchAudio.destroy()
    } catch (e) {
      console.log(e, 'catch')
      // handle the exception
    }
  }

  touchAudio = uni.createInnerAudioContext()
  touchAudio.autoplay = false
  touchAudio.src = playList[type]
  touchAudio.onCanplay(() => {
    touchAudio.play()
  })

  touchAudio.onError(() => {
    touchAudio.destroy()
  })

  touchAudio.onPause(() => {
    touchAudio.destroy()
  })

  touchAudio.onStop(() => {
    touchAudio.destroy()
  })

  touchAudio.onEnded(() => {
    setTimeout(() => {
      touchAudio.destroy()
    }, 500)
  })
}

onUnload(() => {
  if (touchAudio) {
    try {
      touchAudio.stop()
      touchAudio.destroy()
      touchAudio = null
    } catch (e) {
      // handle the exception
    }
  }
})

const px2upx = (px: number) => {
  //计算比例
  const scale = uni.upx2px(100) / 100
  return px / scale
}
</script>
<style lang="scss" scoped>
@keyframes startdownanimation {
  0% {
    opacity: 0.2;
    transform: scale(1);
  }

  50% {
    opacity: 1;
    transform: scale(1.5);
  }

  100% {
    opacity: 0;
    transform: scale(1);
  }
}

@keyframes rotatetwo {
  0% {
    transform: scale(0.9);
  }

  50% {
    transform: scale(1);
  }

  100% {
    transform: scale(0.9);
  }
}

.box {
  position: relative;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100vh;
  overflow: hidden;
  color: #fff;
  background-color: rgb(31 89 177 / 40%);
  will-change: transform;
}

.main {
  position: relative;
  display: flex;
  flex-direction: column;
  // justify-content: space-between;
  width: 100%;
  height: 100%;
  padding: 0 48rpx 0 50rpx;
}

.main-header {
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100%;
  padding: 32rpx 48rpx;
}

.match-box {
  // position: absolute;
  // top: 0;
  // left: 0;
  width: 100%;
  height: 794rpx;
  // padding: 0 48rpx 0 50rpx;
  margin-bottom: 34rpx;
}

.match-border {
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #fff;
  border-radius: 32rpx;
}

.target {
  position: absolute;
  width: 164rpx;
  height: 164rpx;
}

.game-time {
  z-index: 3;
  //   position: absolute;
  display: flex;
  align-items: center;
  justify-content: space-between;
  // width: 100%;
  // height: 40rpx;
  // padding: 0 24rpx;
  width: 654rpx;
  height: 112rpx;
  padding: 0 50rpx 0 52rpx;
  background: #4090f1;
  border: 2rpx solid #fff;
  border-radius: 16rpx;
}

.game-time-col {
  display: flex;
  align-items: center;
}

.game-time-name {
  font-size: 32rpx;
  color: #fff;
}

.game-time-value {
  font-size: 48rpx;
  color: #fff;
}

.mode,
.start-box,
.game-over,
.start-down {
  position: absolute;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-evenly;
  width: 100%;
  height: 100%;
  background: rgb(0 0 0 / 50%);
}

.start-down {
  background: rgb(0 0 0 / 70%);
}

.start-down-name {
  font-size: 200rpx;
  font-style: normal;
  font-weight: 600;
  color: #fff;
  text-align: center;
  text-shadow: 0 4rpx 0 rgb(0 0 0 / 60%);
  text-transform: none;
  animation: startdownanimation 1s ease-in-out infinite;
}

.start-box .start-btn,
.reset-btn,
.quit-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 517.46rpx;
  height: 151.76rpx;
  animation: rotatetwo 1s infinite;

  .mode-name {
    z-index: 3;
    margin-bottom: 30rpx;
    text-shadow: 0 2rpx 0 rgb(0 0 0 / 60%);
  }
}

.mode-content {
  display: flex;
  flex-direction: column;
}

.mode-easy,
.mode-primary,
.mode-difficult {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 517.46rpx;
  height: 151.76rpx;
  margin-bottom: 64rpx;
  animation: rotatetwo 1s infinite;

  .mode-name {
    z-index: 3;
    margin-bottom: 30rpx;
    text-shadow: 0 2rpx 0 rgb(0 0 0 / 60%);
  }
}

.back-image {
  position: absolute;
  z-index: 2;
  width: 100%;
  height: 100%;
}

.mode-name {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 48rpx;
  font-style: normal;
  font-weight: 600;
  color: #fff;
  text-shadow: 0 4rpx 0 rgb(0 0 0 / 60%);
  text-transform: none;
}

.over-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-evenly;
  height: 100%;
}

.over-main {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;

  .over-name {
    font-size: 64rpx;
    font-style: normal;
    line-height: 75rpx;
    color: #fff;
    text-align: center;
    text-shadow: 0 8rpx 0 rgb(0 0 0 / 60%);
    text-transform: none;
  }

  .game-over-time-box {
    display: flex;
    align-items: flex-end;
    margin-top: 72rpx;
  }

  .time-name {
    font-size: 48rpx;
    font-style: normal;
    line-height: 56rpx;
    color: #fff;
    text-align: center;
    text-shadow: 0 10rpx 0 rgb(0 0 0 / 60%);
    text-transform: none;
  }

  .game-over-time {
    font-size: 240rpx;
    font-style: normal;
    line-height: 281rpx;
    color: #fff;
    text-align: center;
    text-shadow: 0 24rpx 0 rgb(0 0 0 / 60%);
    text-transform: none;
  }
}

.shape-box {
  width: 653rpx;
  height: 293rpx;
  background: #fff;
  border-radius: 32rpx;

  .shape-content-box {
    display: flex;
    height: 100%;
    padding: 16rpx 36rpx;
  }
}

// 各类形状
// 正方形
.square {
  width: 164rpx;
  height: 164rpx;
  background-color: #a1c8a7;
}

// 圆
.circle {
  width: 164rpx;
  height: 164rpx;
  background-color: #1e90ff;
  border-radius: 50%;
}

// 三角形
.triangle {
  width: 164rpx;
  height: 164rpx;
  overflow: hidden;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
  background-color: #e9d24e;
}

// 六边形
.hexagon {
  width: 164rpx;
  height: 164rpx;
  overflow: hidden;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  background-color: #1e90ff;
}

.desc {
  padding-left: 24rpx;
  font-size: 36rpx;
}
</style>
